﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using OverStore.Configuration.Builder;
using System.Data.SqlServerCe;
using OverStore.Configuration.Builder.Fluent;
using OverStore.Configuration.Components;
using OverStore.Exceptions;
using OverStore.Runtime.Storage;
using OverStore.Runtime.ObjectRepository.ErrorTransformer;

namespace OverStore.TutorialAsTest.UnitTest
{
    [TestClass]
    public class ClosedGraphs
    {
        // create table SelfRef (id int not null primary key, name nvarchar(100), refId int null);
        // alter table SelfRef Add Constraint uq_selfref_name unique(name);
        // alter table SelfRef add constraint fk_selfref_selfref foreign key (refId) references SelfRef(id);

        class SelfRef
        {
            public int Id { get; set; }

            public string Name { get; set; }

            public SelfRef Ref { get; set; }

            public override string ToString()
            {
                return Name;
            }
        }

        private IPersistenceSessionFactory CreateConfig(string connectionString)
        {
            return OverStoreFluent.Configure()
                .LogToDebug()
                .ConnectToDatabase<SqlCeConnection>(connectionString, ProcessOpenedReadersMode.ForciblyCloseReaders )
                .MapClassToTable<SelfRef, int>("SelfRef")
                    .MapKey("id", e => e.Id, (e, v) => e.Id = v, Component.IdentifierGenerator.Seq<SelfRef>(-1, -1))
                    .MapScalarValue<string>("Name", e => e.Name, (e, v) => e.Name = v)
                    .MapNullableReference<SelfRef, int>("refId", e => e.Ref, (e, v) => e.Ref = v)
                    .TransformError<SqlCeException>("uq_selfref_name", MessageMatchType.MessageContainsPattern, "Name is not unique")
                    .ConfigureAttacher()
                        .ExtractIdentifier(e => e.Id)
                        .AlwaysGenerateNewIdentifier()
                        .NewWhenDefaultIdentifier()
                        .ForceDataChanged()
                    .End()
                .EndTableMapping()
            .End();
        }

        [TestMethod]
        public void ClosedGraphRead()
        {
            var cs = TestHelper.CreateTestDbCopyAndBuildConnectionString();
            using (var session = CreateConfig(cs).CreateSession())
            {
                var one = session.GetByIdentifier<SelfRef, int>(1);
                Assert.AreEqual(1, one.Id);
                Assert.AreEqual("one", one.Name);
                Assert.IsNull(one.Ref);

                var oneone = session.GetByIdentifier<SelfRef, int>(11);
                Assert.AreEqual(11, oneone.Id);
                Assert.AreEqual("one-one", oneone.Name);
                Assert.IsNotNull(oneone.Ref);

                var three = session.GetByIdentifier<SelfRef, int>(3);
                Assert.AreEqual(3, three.Id);
                Assert.AreEqual("three", three.Name);
                Assert.AreSame(oneone, three.Ref);
                Assert.AreSame(three, oneone.Ref);

                var two = session.GetByIdentifier<SelfRef, int>(2);
                Assert.AreEqual(2, two.Id);
                Assert.AreEqual("two", two.Name);
                Assert.AreSame(two, two.Ref);
            }
        }

        [TestMethod]
        public void ClosedGraphUpdate()
        {
            var cs = TestHelper.CreateTestDbCopyAndBuildConnectionString();
            using (var session = CreateConfig(cs).CreateSession())
            {
                var oneone = session.GetByIdentifier<SelfRef, int>(11);
                var three = session.GetByIdentifier<SelfRef, int>(3);
                oneone.Name += "1";
                three.Name = "changed";
                session.Save(oneone);
            }
            using (var session = CreateConfig(cs).CreateSession())
            {
                var three = session.GetByIdentifier<SelfRef, int>(3);
                Assert.AreEqual("changed", three.Name);
            }
        }

        [TestMethod]
        public void ClosedGraphsUpdateAfterError()
        {
            var cs = TestHelper.CreateTestDbCopyAndBuildConnectionString();
            using (var session = CreateConfig(cs).CreateSession())
            {
                var oneone = session.GetByIdentifier<SelfRef, int>(11);                
                var three = session.GetByIdentifier<SelfRef, int>(3);
                oneone.Name = "one1";
                three.Name = "one1";
                try
                {
                    session.Save(oneone);
                    Assert.Fail("Must throw error because name is duplicated!");
                }
                catch (PersistenceOperationFailedException ex)
                {
                    Assert.AreEqual("Name is not unique", ex.Message);
                }
                three.Name = "changed";
                session.Save(oneone);
            }
            using (var session = CreateConfig(cs).CreateSession())
            {
                var three = session.GetByIdentifier<SelfRef, int>(3);
                Assert.AreEqual("changed", three.Name);
            }
        }

        [TestMethod]
        public void ClosedGraphsUpdateAfterErrorSelfRef()
        {
            var cs = TestHelper.CreateTestDbCopyAndBuildConnectionString();
            using (var session = CreateConfig(cs).CreateSession())
            {
                var two = session.GetByIdentifier<SelfRef, int>(2);
                two.Name = "one";
                try
                {
                    session.Save(two);
                    Assert.Fail("Must throw error because name is duplicated!");
                }
                catch (PersistenceOperationFailedException ex)
                {
                    Assert.AreEqual("Name is not unique", ex.Message);
                }
                two.Name = "changed";
                session.Save(two);
            }
            using (var session = CreateConfig(cs).CreateSession())
            {
                var two = session.GetByIdentifier<SelfRef, int>(2);
                Assert.AreEqual("changed", two.Name);
            }
        }

        [TestMethod]
        public void ClosedGraphsReload()
        {
            var cs = TestHelper.CreateTestDbCopyAndBuildConnectionString();
            using (var session = CreateConfig(cs).CreateSession())
            {
                var oneone = session.GetByIdentifier<SelfRef, int>(11);
                oneone.Name = "changed";
                oneone.Ref.Name = "changed";
                session.Reload(oneone);

                Assert.AreEqual("one-one", oneone.Name);
                Assert.AreNotEqual("three", oneone.Ref.Name);
            }
        }
    }
}
